//proto文件生成器，主要生成Unity内调用API

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Google.Protobuf;
using UnityEngine;
using UnityEditor;

public class ProtoGenerator : Singleton<ProtoGenerator>
{
    ProtoParser parser = new ProtoParser();
    ProtoCodeGenerator generator = new ProtoCodeGenerator();
    FileStream file;
    // API类型文件输出目录
    string outFile_dir = EditorPathMgr.Instance.ProtoOutPath;
    #if UNITY_EDITOR_WIN
    string protoc = Application.dataPath.Replace("/Assets","") + "/Proto/Bin/protoc";
    #elif UNITY_EDITOR_OSX
    string protoc = Application.dataPath.Replace("/Assets","") + "/Proto/Bin/protoc_mac";
    #elif UNITY_EDITOR_LINUX
    string protoc = Application.dataPath.Replace("/Assets","") + "/Proto/Bin/protoc";
    #endif
    // protoc.exe文件路径
    // string protoc = "./Proto/Bin/protoc";
    #if UNITY_EDITOR_WIN
    string ShellExe = "cmd.exe";
    #else
    string ShellExe = "/bin/zsh";
    #endif

    public List<string> Names = new List<string>();

    public void Generate(string protoPath)
    {
        string protoContent = File.ReadAllText(protoPath);
        ProtoContent content = parser.Parse(protoContent);
        if(content.ReqMsg.Count == 0 && content.ResMsg.Count == 0)
        {
            Debug.Log($"{protoPath}文件没有需要生成的协议");
            return;
        }
        generator.SetProtoStruct(content);
        Names.Add(content.NameSpace);
        var fileName = Path.GetFileName(protoPath).Replace(".proto","");

        if(!Directory.Exists(outFile_dir))
        {
            UnityEngine.Debug.Log("创建目录");
            var dir = Directory.CreateDirectory(outFile_dir);
            dir.Create();
        }
        var current_dir = $"{outFile_dir}/{fileName}/Net";
        if(!Directory.Exists(current_dir))
        {
            UnityEngine.Debug.Log("创建目录");
            var dir = Directory.CreateDirectory(current_dir);
            dir.Create();
        }
        Debug.Log(fileName);
        var out_file = $"{current_dir}/{fileName}Net.cs";
        string old_code = "";
        if(!File.Exists(out_file))
        {

            file = File.Create(out_file);
            file.Close();
        }
        else
        {
            old_code = File.ReadAllText(out_file);
        }
        string code = generator.WriteFileCode(old_code);
        // Debug.Log(code);
        file = File.Open(out_file,FileMode.OpenOrCreate);
        byte[] bytes = Encoding.UTF8.GetBytes(code);
        // clear file content
        file.SetLength(0);
        file.Write(bytes,0,bytes.Length);
        file.Close();
        generator.Clear();
    }

    public void CheckOutWithConvertProtoFile()
    {
        Names.Clear();
        var p = Shell.Instance.CreateShell(ShellExe, "git status -s");

        var status = p.StandardOutput.ReadToEnd();
        var error = p.StandardError.ReadToEnd();
        p.Close();
        Debug.Log(status);
        if(error != "")
        {
            Debug.LogError(error);
            return;
        }

        var lines = status.Split('\n');
        List<string> protoFiles = new List<string>();
        //get all changed proto files
        foreach(var line in lines)
        {
            var file = line;
            if(line.EndsWith(".proto"))
            {
                if(line.StartsWith(" D"))
                {
                    continue;
                }
                    
                file = line.Replace(" M","");
                file = file.Replace("??","");
                file = file.Replace("A ","");
                file = file.Trim();
                protoFiles.Add(file);
            }
        }

        foreach(var file in protoFiles)
        {
            var out_proto_dir = $"{outFile_dir}/{parser.ParseNameSpace(file)}/Net";
            if(!Directory.Exists(out_proto_dir))
            {
                Directory.CreateDirectory(out_proto_dir);
            }
            var cmd = $"{protoc} --proto_path=./ {file} --csharp_out={out_proto_dir}";
            // Debug.Log();
            p = Shell.Instance.CreateShell(ShellExe,cmd);
            var err = p.StandardError.ReadToEnd();
            if(err != "")
            {
                Debug.LogError(err);
                p.Close();
                return;
            }
            p.Close();
            Generate(file);
        }
        BootGenerator.Instance.UpdateProto(Names);
        CompileMgr.Instance.CallRecompile();
    }

    public void GenerateAllProtoFile()
    {
        Names.Clear();
        // find all file
        var files = Directory.GetFiles("./Proto");
        
        foreach(var file in files)
        {
            // ProtoGenerator.Instance.Generate(file);
            Debug.Log(parser.ParseNameSpace(file));
            var fileName = Path.GetFileName(file).Replace(".proto","");
            var out_proto_dir = $"{outFile_dir}/{fileName}/Net";
            if(!Directory.Exists(out_proto_dir))
            {
                Directory.CreateDirectory(out_proto_dir);
            }
            var cmd = $"{protoc} --proto_path=./ {file} --csharp_out={out_proto_dir}";
            var p = Shell.Instance.CreateShell(ShellExe,cmd);
            var err = p.StandardError.ReadToEnd();
            if(err != "")
            {
                Debug.LogError(err);
                p.Close();
                return;
            }
            p.Close();
            Generate(file);
        }

        BootGenerator.Instance.UpdateProto(Names);
        CompileMgr.Instance.CallRecompile();
    }
}